package com.ruoyi.common.utils.file

import com.ruoyi.common.config.RuoYiConfig
import com.ruoyi.common.constant.Constants
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException
import com.ruoyi.common.exception.file.FileSizeLimitExceededException
import com.ruoyi.common.exception.file.InvalidExtensionException
import com.ruoyi.common.exception.file.InvalidExtensionException.*
import com.ruoyi.common.utils.DateUtils.datePath
import com.ruoyi.common.utils.MyStringUtils
import com.ruoyi.common.utils.uuid.Seq
import org.apache.commons.io.FilenameUtils
import org.springframework.web.multipart.MultipartFile
import java.io.*
import java.nio.file.Paths
import java.util.*

/**
 * 文件上传工具类
 *
 * @author ruoyi
 */
object FileUploadUtils {
    /**
     * 默认大小 50M
     */
    const val DEFAULT_MAX_SIZE = (50 * 1024 * 1024).toLong()

    /**
     * 默认的文件名最大长度 100
     */
    private const val DEFAULT_FILE_NAME_LENGTH = 100

    /**
     * 默认上传的地址
     */
    private var defaultBaseDir: String? = RuoYiConfig.profile

    /**
     * 以默认配置进行文件上传
     *
     * @param file 上传的文件
     * @return 文件名称
     * @throws Exception
     */
    @Throws(IOException::class)
    fun upload(file: MultipartFile): String {
        return try {
            upload(defaultBaseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION)
        } catch (e: Exception) {
            throw IOException(e.message, e)
        }
    }

    /**
     * 根据文件路径上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @return 文件名称
     * @throws IOException
     */
    @Throws(IOException::class)
    fun upload(baseDir: String, file: MultipartFile): String {
        return try {
            upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION)
        } catch (e: Exception) {
            throw IOException(e.message, e)
        }
    }

    /**
     * 文件上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException 比如读写文件出错时
     * @throws InvalidExtensionException 文件校验异常
     */
    @Throws(FileSizeLimitExceededException::class, IOException::class, FileNameLengthLimitExceededException::class, InvalidExtensionException::class)
    fun upload(baseDir: String?, file: MultipartFile, allowedExtension: Array<String>?): String {
        val fileNamelength = Objects.requireNonNull(file.originalFilename).length
        if (fileNamelength > DEFAULT_FILE_NAME_LENGTH) {
            throw FileNameLengthLimitExceededException(DEFAULT_FILE_NAME_LENGTH)
        }
        assertAllowed(file, allowedExtension)
        val fileName = extractFilename(file)
        val absPath = getAbsoluteFile(baseDir, fileName).absolutePath
        file.transferTo(Paths.get(absPath))
        return getPathFileName(baseDir, fileName)
    }

    /**
     * 编码文件名
     */
    fun extractFilename(file: MultipartFile): String {
        return MyStringUtils.format("{}/{}_{}.{}", datePath(),
                FilenameUtils.getBaseName(file.originalFilename), Seq.getId(Seq.uploadSeqType), getExtension(file))
    }

    @Throws(IOException::class)
    fun getAbsoluteFile(uploadDir: String?, fileName: String): File {
        val desc = File(uploadDir + File.separator + fileName)
        if (!desc.exists()) {
            if (!desc.parentFile.exists()) {
                desc.parentFile.mkdirs()
            }
        }
        return desc
    }

    @Throws(IOException::class)
    fun getPathFileName(uploadDir: String?, fileName: String): String {
        val dirLastIndex: Int = RuoYiConfig.profile?.length!! + 1
        val currentDir = MyStringUtils.mySubstring(uploadDir, dirLastIndex)
        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName
    }

    /**
     * 文件大小校验
     *
     * @param file 上传的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    @Throws(FileSizeLimitExceededException::class, InvalidExtensionException::class)
    fun assertAllowed(file: MultipartFile, allowedExtension: Array<String>?) {
        val size = file.size
        if (size > DEFAULT_MAX_SIZE) {
            throw FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024)
        }
        val fileName = file.originalFilename
        val extension = getExtension(file)
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
            if (allowedExtension.contentEquals(MimeTypeUtils.IMAGE_EXTENSION)) {
                throw InvalidImageExtensionException(allowedExtension, extension!!,
                        fileName)
            } else if (allowedExtension.contentEquals(MimeTypeUtils.FLASH_EXTENSION)) {
                throw InvalidFlashExtensionException(allowedExtension, extension!!,
                        fileName)
            } else if (allowedExtension.contentEquals(MimeTypeUtils.MEDIA_EXTENSION)) {
                throw InvalidMediaExtensionException(allowedExtension, extension!!,
                        fileName)
            } else if (allowedExtension.contentEquals(MimeTypeUtils.VIDEO_EXTENSION)) {
                throw InvalidVideoExtensionException(allowedExtension, extension!!,
                        fileName)
            } else {
                throw InvalidExtensionException(allowedExtension, extension!!, fileName)
            }
        }
    }

    /**
     * 判断MIME类型是否是允许的MIME类型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    fun isAllowedExtension(extension: String?, allowedExtension: Array<String>): Boolean {
        for (str in allowedExtension) {
            if (str.equals(extension, ignoreCase = true)) {
                return true
            }
        }
        return false
    }

    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    fun getExtension(file: MultipartFile): String {
        var extension = FilenameUtils.getExtension(file.originalFilename)
        if (MyStringUtils.isEmpty(extension!!)) {
            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.contentType))
        }
        return extension
    }
}
